Chapter 14 TEXT INPUT/OUTPUT ADA IS NOT BIG ON I/O _________________________________________________________________ Ada was originally designed as a language for embedded real-time systems rather than as a business oriented language, so input and output of data is a rather low priority part of the language. It may surprise you to know that there are no input or output instructions available for use within the Ada language itself, the problem being left to the compiler writers. However, the designers of Ada realized that if the entire topic of input and output were left to the individual compiler writers, users would be left with a mess trying to use nonstandard methods of input and output. They exercised tremendous foresight by defining a standard external library to be used for input and output of data that must be available with all Ada compilers that pass the validation suite. Even though the libraries are defined, the details of their actions are not precisely defined, so there is still some chance that your compiler will not behave in exactly the same way as the descriptions given here. You will have to check the documentation packaged with your compiler to discover the exact details of input and output operation. You may rest assured however, that the differences will only be in the smallest details. With the preceding disclaimer, let's look at some text output procedures. FORMATTED OUTPUT DATA _________________________________________________________________ If you examine the program FORMATS.ADA, you will =============== find that it does very little other than FORMATS.ADA illustrate the various methods of outputting =============== formatted data to the monitor. Several types and variables are declared, then six separate instantiations of Text_IO are declared for use with the six types used in the program. Actually, there is nothing here that is new to you, since we have covered all of these statements before, but they are included here in order to present an example of all of them in one place. A few comments should be made at this point. Note the six instantiations of Text_IO packages given in lines 19 through 31. These are necessary in order to output the various types we are using in this program. The package instantiations must be declared after the various types are declared. One of the instantiations could be eliminated by transforming the type of the MY_INTEGER type variable to type INTEGER prior to outputting it, but this is done for illustration. When we instantiate a package, we are actually Page 14-1 Chapter 14 - Text Input/Output using a copy of a generic package, which will be the subject of a detailed study in part 2 of this tutorial. After you have studied the program and understand it, compile and run it for an elaborate set of formatted output examples. Your compiler may output the float and fixed outputs slightly differently than that given in the result of execution due to different defaults assigned by your compiler. FORMATTED OUTPUT TO A FILE _________________________________________________________________ The example file named EASYOUT.ADA contains =============== examples of how to use a file in a very easy EASYOUT.ADA fashion. Since there is no standard method =============== which is used by all operating systems to name files, we cannot depend on what the rules will be in order to use Ada on any system, so we need two file names in order to write to or read from a file. We need an internal name, which will be used by our program to refer to the file, and which follows the naming rules defined by Ada. We also need an external name which will be used by the operating system to refer to the file and whose name follows the rules dictated by the operating system. In addition to having the two names, we need a way to associate the names with each other, and tell the system that we wish to use the file so named. We do all of this with the Create procedure as shown in line 14 of this program. The variable Turkey, is the internal file name which we declared in line 10 to be of type FILE_TYPE, a type exported by the package Text_IO for just this purpose. The "with Text_IO;" statement in line 2 makes the type FILE_TYPE available in this program. The last argument, which is either a string constant, as it is in this case, or a string variable, gives the external name of the file we wish to create and write to. Note carefully, that if you have a file named TEST.TXT in your default directory, it will be erased when the program is run, because this statement will erase it. The second parameter in this procedure call, is either In_File if you intend to read from the file, or Out_File if you plan to write to it. There is no mode using text I/O in which you can both read from and write to the same file. In this case, we wish to write to the file, so we use the mode Out_File and create the file. WRITING TO THE FILE _________________________________________________________________ Line 16 looks rather familiar to us, since it is our old friend Put_Line with an extra parameter prior to the text we wish to output. Since Turkey is of type FILE_TYPE, the system is smart enough to know that this string will be output to the file with the Page 14-2 Chapter 14 - Text Input/Output internal name of Turkey, and the external name of TEST.TXT, so the string will be directed there along with the "carriage return/line feed". The next line will also be directed there, as will the 2 New_Lines requested in line 18. Line 19 does not have the filename of Turkey as a parameter, so it will be directed to the default output device, the monitor, in the same manner that it has been for all of this tutorial. REDIRECTING THE OUTPUT _________________________________________________________________ Line 21 contains a call to the procedure Set_Output which will make the filename which is given as the actual parameter the default filename. As long as this is in effect, any output without a filename will be directed to the file with the internal filename Turkey. In order to output some data to the monitor, it can still be done, but it must be directed there by using its internal filename as a parameter, the internal filename being Standard_Output. The group of statements given in lines 22 through 25 do essentially the same thing as those in lines 16 through 19. The default is returned to the standard output device in line 26, and the program completes normally. CLOSING THE FILE _________________________________________________________________ The simple statement given in line 29 is used to close the file when we are finished with it. The system only needs to be given the internal name of the file, since it knows the corresponding external filename. Failure to close the file will do no harm in such a simple program, because the operating system will close it automatically, but in a large program, it would be wise to close a file when it is no longer needed. There is an upper limit on how many files can be open at one time, and there is no sense wasting a file allocation on an unneeded file. Be sure to compile and run this program, then check your default directory to see that you have the file named TEST.TXT in it containing the expected text. MULTIPLE FILES OPENED AT ONE TIME _________________________________________________________________ Examine the file MULTOUT.ADA for an example =============== program in which we open and use 3 different MULTOUT.ADA files plus the monitor. The three internal =============== filenames are declared in line 10, and all three are opened in the output mode with three different external names as shown in lines 15, 16, and 17. Page 14-3 Chapter 14 - Text Input/Output We execute the loop in lines 19 through 28, where we write nonsense data to the three files, output a test string to the display in line 30, and finally close all three files. Note that we could have defined one of the files to be the default file and written to it without the filename, then used the filename for the standard output in line 30, achieving the same result. The program is fairly simple, so after you feel you understand it, compile and run it. After a successful run, examine the default directory to see that the three files exist and contain the expected results. Do not erase the generated files, because we will use them as example input files in the next few programs. INPUTTING CHARACTERS FROM A FILE _________________________________________________________________ Examine the file CHARIN.ADA for an example of ================ how to read CHARACTER type data from a file. In CHARIN.ADA this example the file name My_File is declared ================ as a FILE_TYPE variable, then used to open CHARACTS.TXT for reading since the In_File mode is used. In this case, the file must exist, or an exception will be raised. The file pointer is set to the beginning of the file by the Open procedure so we will read the first character in the file the first time we read from the file. The loop in lines 17 through 21 causes us to read a character from the file and display it on the monitor each time we pass through the loop. The function End_Of_File returns a TRUE when the next character to be read is the end of file character for your particular implementation. We do not need to know what the end of file character is, it is up to the compiler writer to find out what it is and return a value of TRUE when the system finds it. The entire file is therefore displayed on the monitor by this program, but with one slight deviation from what you would expect. Because of the way Ada is defined, the end of line characters are simply ignored by the Get procedure, and since they are not read in, they cannot be output to the monitor either. The end result is that the characters are all output in one long string with no line feeds. You will see this later when you compile and run the program. We will continue our study in this program and see a much better way to read and display the data. THE RESET PROCEDURE _________________________________________________________________ The Reset procedure in line 24 resets the file named as a parameter, which simply means that the file pointer is moved to the first character once again. The file is ready to be read in Page 14-4 Chapter 14 - Text Input/Output another time. The loop in lines 26 through 35 is the same as the previous loop except for the addition of another new function. When the end of a line is found, the function End_Of_Line returns a TRUE, and the program displays the special little message in line 30. In addition to the message, the New_Line procedure is called to supply the otherwise missing end of line. Unfortunately, we still have a problem because the End_Of_Line is actually a lookahead function and becomes TRUE when the next character in the buffer is an end of line character. When the program is executed, we find that it does not output the last character of each line in this loop, because we never execute the else clause in the if statement for that character. When the program is executed, you will notice that the periods at the end of the sentences are not displayed. The next loop in this program will illustrate how to input and output the data correctly considering all of the Ada idiosyncrasies. If you recall, we stated at the beginning of this lesson that input and output programming in Ada was at best a compromise. Don't worry, you will be able to input or output anything you wish to very soon. A CORRECT CHARACTER INPUT/OUTPUT LOOP _________________________________________________________________ The file is once again reset in line 38, and a third loop in lines 41 through 49 reads and displays the file on the monitor. This time when we go through the loop, we recognize that the end of line is reading one character ahead in the input file, not the one we just read, and we adjust the program accordingly. We output the character, then check for end of line, and call the New_Line procedure if we detect an end of line. You will see, when you run it, that the last character on the line will be displayed as desired. It should be pointed out to you that the End_Of_File is another lookahead function and must be tested after we output the last character read in. This loop does just that, because the end of file is acted on after the character is output. Be sure to compile and run this program. It would be interesting to edit the input file, CHARACTS.TXT, adding a few characters, then rerunning the program to see how it handles the new data. STRING INPUT AND OUTPUT _________________________________________________________________ The next example program, STRINGIN.ADA, illustrates how to read a string from an input file. In much the same manner as for the character, the end of line character is ignored when encountered Page 14-5 Chapter 14 - Text Input/Output in the input file. The result is that when the ================ first loop is run, no "carriage returns" are STRINGIN.ADA issued and the text is all run together. The ================ second loop, however, uses the lookahead method and calls the New_Line procedure when it detects the end of line in the input stream. The big difference in this program and the last is the fact that a STRING type variable is used for input here and a CHARACTER type variable was used in the last one. I PLAYED A TRICK ON YOU _________________________________________________________________ This program has a bit of a trick in it. The input file contains an exact multiple of 10 characters in each line, and the input variable, of type STRING, has ten characters in it. There is therefore no real problem in reading into the ten character string, but if the lines were not exact multiples of ten characters, we would have gotten an input error. If you remember how difficult the STRING variable was to work with when we studied strings, you will understand that we have the same problem here. When we get to chapter 16 with its example programs, we will see how we can greatly improve the string capability with an add-on library of dynamic string routines. Until then, simply realize that string input is possible, but don't get too excited about it. Be sure to compile and run this program, then add a few characters to some of the lines in the file named CHARACTS.TXT to see the result of trying to read in an odd group of extra characters. NOW TO READ IN SOME NUMBERS _________________________________________________________________ Computers are very good at working with numbers, =============== and in order to work with them, we must have a INTIN.ADA way to get them into the computer. The example =============== file INTIN.ADA illustrates how to read INTEGER type data into the computer from a file. Much like the last two programs, we open a file for reading, and begin executing a loop where we read an INTEGER type number as input each time through the loop. We are reading an INTEGER type number because that is the type of Index, the second actual parameter in the Get procedure call. Since it is an INTEGER type number, the system will begin scanning until it finds a valid INTEGER type number terminated by a space. It will then return that number, in the computer's internal format, assigned to the variable we supplied to it, and make it available for our use like any other INTEGER type variable. If any data other than INTEGER type data were to be encountered in the input file before it found a valid INTEGER value, an exception would be raised, and the program would be terminated. Page 14-6 Chapter 14 - Text Input/Output The loop continues in much the same manner as the last two programs until it detects the end of file, at which time it stops. There is one other slight difference when using numeric inputs, the system does not read over the end of line character, because it doesn't know how to get across it. It gets stuck trying to read the next data point but never gets to it because the end of line is blocking it. When the end of line is detected, or at any other time, you can issue the function Skip_Line which tells it to go to the beginning of the next line for its next input data. At any time then, you can begin reading on the next line for your next data point. Be sure to compile and run this program, then change the spacing of some of the numbers in the input file to see that they are read in with a free form spacing. WHAT ABOUT FLOATING POINT DATA INPUTS? _________________________________________________________________ Once you understand the method of reading INTEGER type data in, you can read any other types in by using the same methods. Just remember that the data you read in must be of the same type as the variable into which it is being read and you will have little difficulty. WHAT ABOUT READING FROM THE KEYBOARD? _________________________________________________________________ Reading from the keyboard is similar to reading from a file, except that it uses the predefined internal filename, Standard_Input, and if you omit a filename reference, the input will come from the keyboard. It should be pointed out that the operating system will probably buffer the input data automatically and give it to your program as a complete string when you hit the return key. It will allow you to enter a complete string of as many integer values as you desire, and when you hit the return, the entire string will be input and processed. Modify the last file to read from the keyboard, and enter some numbers after compiling and running it. Note that there is no End_Of_File when reading from the keyboard. Use a loop with a fixed number of passes, or use an infinite loop and quit when a certain number is input such as -1. HOW DO WE PRINT DATA ON THE PRINTER? _________________________________________________________________ The program named PRINTOUT.ADA illustrates how ================ to send data to your printer. The only PRINTOUT.ADA difference in sending data to the printer, from ================ sending data to a disk file, is the use of the predefined name LPT1 for the external file name. Page 14-7 Chapter 14 - Text Input/Output This file is defined as the printer rather than a file, and as you can see from the program, it operates no differently than any other file. Other names may be applicable for the printer also, such as PRN, COM1, etc. Check your documentation for the predefined names that mimic a file. Be sure your printer is on line, then compile and run this program. TEXT IO PRAGMAS _________________________________________________________________ A pragma is a suggestion to the compiler and can be ignored by any implementation according to the definition of Ada. Refer to appendix B of the LRM for the definition of the predefined pragmas LIST and PAGE. Since these may not be available with any particular compiler, their use should be discouraged in order to attain a high degree of program portability. PAGE AND LINE CONTROL SUBPROGRAMS _________________________________________________________________ Refer to the definition of Text_IO in the LRM for a large assortment of page and line control subprograms for use with text I/O. These are required to be available with every compiler, so you should study them to gain an insight into the page and line control subprograms available to you in any Ada program. There should not be anything too difficult for you to understand in these definitions. PROGRAMMING EXERCISES _________________________________________________________________ 1. Modify CHARIN.ADA to read from the keyboard and echo data to the monitor until a Q is entered. 2. Write a program named FLOATIN.ADA that reads floating point data from a file and displays it. There is no standard on floating point input. Some compilers require a decimal point with at least one digit before and after the decimal point. Some may not even require a decimal point but will supply it to an input that looks like an INTEGER value. Experiment and determine the characteristics of your compiler. Page 14-8